#pragma once

namespace rt
{
struct Material;
struct Ray;
struct Ray4;
struct Ray4_SingleOrigin;

struct IntersectionData;

enum ObjectType
{
	OBJECT_NONE,
	OBJECT_SPHERE,
	OBJECT_PLANE,
	OBJECT_BOX,
	OBJECT_BLOB,
	OBJECT_ALGEB
};

struct Sphere
{
	void* operator new[] (size_t size)
	{
		void * p = _aligned_malloc(size, 16);
		if (p == 0) LOG("ACHTUNG!!! OUT OF MEMORY!!!");
		return p;
	}
	void operator delete[] (void *p)
	{
		_aligned_free(p);
	}

	Sphere()
	{
		SetCenter(XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
		SetRadiusSquared(1.0f);
	}
	void SetCenter(XMVECTOR center)
	{
		CenterAndRadiusSquared = XMVectorSetW(center, XMVectorGetW(CenterAndRadiusSquared));
	}
	void SetRadiusSquared(float radius)
	{
		CenterAndRadiusSquared = XMVectorSetW(CenterAndRadiusSquared, radius);
	}
	XMVECTOR CenterAndRadiusSquared;
};

struct Plane
{
	void* operator new[] (size_t size)
	{
		void * p = _aligned_malloc(size, 16);
		if (p == 0) LOG("ACHTUNG!!! OUT OF MEMORY!!!");
		return p;
	}
	void operator delete[] (void *p)
	{
		_aligned_free(p);
	}

	Plane()
	{
		SetPointAndNormal(XMVectorSet(0.0f, -5.0f, 0.0f, 0.0f), XMVectorSet(0.0f, 1.0f ,-0.0f, 0.0f));
	}
	void SetPointAndNormal(XMVECTOR point, XMVECTOR normal)
	{
		NormalAndD = XMVectorSetW(normal, XMVector3Dot(point, normal).m128_f32[0]);
	}
	XMVECTOR NormalAndD;
};

struct Box
{
	void* operator new[] (size_t size)
	{
		void * p = _aligned_malloc(size, 16);
		if (p == 0) LOG("ACHTUNG!!! OUT OF MEMORY!!!");
		return p;
	}
	void operator delete[] (void *p)
	{
		_aligned_free(p);
	}

	Box()
	{
		SetPositionAndSize(XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f), XMVectorSet(1.0f, 3.0f, 1.0f, 0.0f));
	}
	void SetPositionAndSize(XMVECTOR pos, XMVECTOR size)
	{
		float x, y, z, px, py, pz;
		x = XMVectorGetX(size);
		y = XMVectorGetY(size);
		z = XMVectorGetZ(size);
		px = XMVectorGetX(pos);
		py = XMVectorGetY(pos);
		pz = XMVectorGetZ(pos);

		Position = pos;

		Planes[0].SetPointAndNormal(XMVectorSet(px + x, 0.0f, 0.0f, 0.0f), XMVectorSet( 1.0f, 0.0f, 0.0f, 0.0f));
		Planes[1].SetPointAndNormal(XMVectorSet(px - x, 0.0f, 0.0f, 0.0f), XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f));
		//Planes[0].SetPointAndNormal(XMVectorSet( 1.0f, 0.0f, 1.0f, 0.0f), XMVector3Normalize(XMVectorSet( 1.0f, 0.0f, 1.0f, 0.0f)));
		//Planes[1].SetPointAndNormal(XMVectorSet(-1.0f, 0.0f, -1.0f, 0.0f), XMVector3Normalize(XMVectorSet(-1.0f, 0.0f, -1.0f, 0.0f)));

		Planes[2].SetPointAndNormal(XMVectorSet(0.0f, py + y, 0.0f, 0.0f), XMVectorSet(0.0f,  1.0f, 0.0f, 0.0f));
		Planes[3].SetPointAndNormal(XMVectorSet(0.0f, py - y, 0.0f, 0.0f), XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f));

		Planes[4].SetPointAndNormal(XMVectorSet(0.0f, 0.0f, pz + z, 0.0f), XMVectorSet(0.0f, 0.0f,  1.0f, 0.0f));
		Planes[5].SetPointAndNormal(XMVectorSet(0.0f, 0.0f, pz - z, 0.0f), XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f));
		//Planes[4].SetPointAndNormal(XMVectorSet( 1.0f, 0.0f, -1.0f, 0.0f), XMVector3Normalize(XMVectorSet( 1.0f, 0.0f, -1.0f, 0.0f)));
		//Planes[5].SetPointAndNormal(XMVectorSet( -1.0f, 0.0f, 1.0f, 0.0f), XMVector3Normalize(XMVectorSet( -1.0f, 0.0f, 1.0f, 0.0f)));
	}
	Plane Planes[6];
	XMVECTOR Position;
};

void IntersectSpheres_All_4RaysSingleOrigin(const Sphere * spheres, int num, const Ray4_SingleOrigin & rays, XMVECTOR & intersIndex, XMVECTOR & depth);
void IntersectSpheres_All_4Rays(const Sphere * spheres, int num, const Ray4 & rays, XMVECTOR & intersIndex, XMVECTOR & depth, XMVECTOR & intersType);
XMVECTOR IntersectSpheres_Any_4Rays(const Sphere * spheres, int num, const Ray4 & rays, XMVECTOR & maxDepth);

void IntersectSpheres_All_1Ray(const Sphere * spheres, int num, const Ray & ray, XMVECTOR & intersIndex, XMVECTOR & depth);
bool IntersectSpheres_Any_1Ray(const Sphere * spheres, int num, const Ray & ray, XMVECTOR & maxDepth);


void IntersectPlanes_All_4RaysSingleOrigin(const Plane * planes, int num, const Ray4_SingleOrigin & rays, XMVECTOR & intersIndex, XMVECTOR & depth);
void IntersectPlanes_All_4Rays(const Plane * planes, int num, const Ray4 & rays, XMVECTOR & intersIndex, XMVECTOR & depth, XMVECTOR & intersType);
XMVECTOR IntersectPlanes_Any_4Rays(const Plane * planes, int num, const Ray4 & rays, XMVECTOR & maxDepth);

void IntersectPlanes_All_1Ray(const Plane * planes, int num, const Ray & ray, XMVECTOR & intersIndex, XMVECTOR & depth);
bool IntersectPlanes_Any_1Ray(const Plane * planes, int num, const Ray & ray, XMVECTOR & maxDepth);


void IntersectBoxes_All_4RaysSingleOrigin(const Box * boxes, int num, const Ray4_SingleOrigin & rays, XMVECTOR & intersIndex, XMVECTOR & depth);
void IntersectBoxes_All_4Rays(const Box * boxes, int num, const Ray4 & rays, XMVECTOR & intersIndex, XMVECTOR & depth, XMVECTOR & intersType);
void IntersectBoxes_All_1Ray(const Box * boxes, int num, const Ray & ray, XMVECTOR & intersIndex, XMVECTOR & depth);
bool IntersectBoxes_Any_1Ray(const Box * boxes, int num, const Ray & ray, XMVECTOR & maxDepth);

//void IntersectSpheres_All_1Ray(const CSphere * spheres, int num, const Ray & ray, uint & intersIndex, float & depth);
//bool IntersectSpheres_Any_1Ray(const CSphere * spheres, int num, const Ray & ray, float & maxDepth);
//
//void IntersectPlanes_All_1Ray(const CPlane * planes, int num, const Ray & ray, uint & intersIndex, float & depth);
//bool IntersectPlanes_Any_1Ray(const CPlane * planes, int num, const Ray & ray, float & maxDepth);

void GetSphereIntersectionData(const Sphere & sphere, XMVECTOR intersPoint, IntersectionData & data);
void GetPlaneIntersectionData(const Plane & plane, XMVECTOR intersPoint, IntersectionData & data);
void GetBoxIntersectionData(const Box & box, XMVECTOR intersPoint, IntersectionData & data);

}